package crap;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;

import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JFrame;

import tools.Arrow;
import tools.ColorConversions;
import tools.Derivative;
import tools.Gauss;

public class Aufgabe03s {
	static int count = 2000;
	public static void main(String[] args) {
	
		try {
			final BufferedImage kermit = ImageIO.read(Thread.currentThread().getContextClassLoader().getResourceAsStream("res/kermit001.jpg"));
			final int w = kermit.getWidth(), h = kermit.getHeight(), w2 = w/2, h2 = h/2;
			int n = 16;

			final BufferedImage bi = kermit.getSubimage(w2, h2, n, n);
			
			int[] pixels = bi.getRGB(0, 0, n, n, new int[n*n], 0, n);
			int[] lum = ColorConversions.getFastLuminance(pixels, new int[pixels.length]);
			
			int[] dx = Derivative.dx(lum, n, n, new int[lum.length]);
			int[] dy = Derivative.dy(lum, n, n, new int[lum.length]);
			
			double[] lengths = Derivative.getLengths(dx, dy, new double[dx.length]);
			double[] angles = Derivative.getAngles(dx, dy, new double[dx.length]);
			double[] kernel = Gauss.buildGaussianKernel(n, 1.5, new double[n*n]);
			
			for (int i = 0; i < kernel.length; i++) {
				lengths[i] *= kernel[i];
 			}
			
			final ArrayList<double[]> descriptor = new ArrayList<double[]>();
			final double[] ang = new double[8];
			final double[] delta = new double[8];
			for (int i = 0; i<ang.length;i++) {
				ang[i] = ((i*2*Math.PI))/8-Math.PI;
//				System.out.println(ang[i]);
			}
			for (int j = 0; j<16; j+=4) {
				for (int i = 0; i<16; i+=4) {
					double[] histo = new double[8];
					for (int x = 0; x<4; x++) {
						for (int y = 0; y<4; y++) {
							
							double angle = angles[i+j*n+x+y*n];
							
							for (int g = 0; g<histo.length; g++) {
								delta[g] = Math.abs(angle-ang[g]);
							} 
							int minIndex = 0;
							double min = Double.POSITIVE_INFINITY;
							for (int g = 0; g<histo.length; g++) {
								if (delta[g] < min) {
									minIndex = g;
									min = delta[g];					
								}
							} 
							histo[minIndex] += lengths[i+j*n+x+y*n];
						}
					}
					descriptor.add(histo);
				}
			}
			
			double max = 0;
			
			for (int i = 0; i<descriptor.size(); i++) {
				double[] bubu = descriptor.get(i);
				for (int j = 0; j<bubu.length; j++) {
					max = Math.max(bubu[j], max);
				}
			}
			for (int i = 0; i<descriptor.size(); i++) {
				double[] bubu = descriptor.get(i);
				for (int j = 0; j<bubu.length; j++) {
					bubu[j]/=max;
					bubu[j]*=100;
				}
			}
		
			for (int i = 0; i<descriptor.size(); i++) {
				double[] bubu = descriptor.get(i);
				for (int j = 0; j<bubu.length; j++) {
					System.out.print(bubu[j]+" ");
				}
			}
			max = 0;
			for (int j = 0; j < lengths.length; j++) {
				max = Math.max(max, lengths[j]);
			}
			for (int j = 0; j < lengths.length; j++) {
				lengths[j] = 255.*(lengths[j]/max);
			}
//			Derivative.prepareLengths(lengths, lum);
			for (int i = 0; i < kernel.length; i++) {
				lum[i] = (int)lengths[i];
			}
			pixels = ColorConversions.buildRGB(lum, lum, lum, pixels);
			bi.setRGB(0, 0, n, n, pixels, 0, n);

			
			JFrame ff = new JFrame();
			ff.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
			Point2D a = new Point2D.Double(100,100), b = new Point2D.Double(500,300);
			
			double arrowWidth = 10, arrowHeight = 40;
			
			final Arrow arrw = new Arrow();
			arrw.setArrow(a, b, arrowWidth, arrowHeight);
			final AffineTransform at = new AffineTransform();
			final AffineTransform translator = new AffineTransform();
			
			final AffineTransform rotat0r = new AffineTransform();
			final Point2D centre = new Point2D.Double(100,100);
			final JComponent canvas = new JComponent() {
				{setPreferredSize(new Dimension(800,800));}
				Line2D line = new Line2D.Double();
				@Override
				public void paint(Graphics g) {
					Graphics2D g2 = (Graphics2D)g;
				    g2.transform(at);
				    g2.drawImage(bi, AffineTransform.getScaleInstance(50, 50), this);

					g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
				    g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
				    g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
				    Point2D point = new Point2D.Double();
				    Point2D centransform = new Point2D.Double();
				    
				    for (int j = 0; j < Math.min(descriptor.size(), count); j++) {
				    	double shiftx = (j%4)*200, shifty = (j/4)*200; 
				    	line.setLine(0, shifty, 800, shifty);
				    	g2.setColor(Color.cyan);
				    	g2.draw(line);
				    	line.setLine(shiftx, 0, shiftx, 800);
				    	g2.draw(line);
				    	double[] histo = descriptor.get(j);
					    for (int i = 0; i < histo.length; i++) {
							point.setLocation(1,0);
							double rot = ang[i];
//							System.out.println(Math.toDegrees(rot));
							rotat0r.setToRotation(rot);
							rotat0r.transform(point, point);
//							translator.transform(centre, centransform);
							point.setLocation(histo[i]*point.getX()+centre.getX()+shiftx, histo[i]*point.getY()+centre.getY()+shifty);
//							translator.transform(point, point);
							centransform = new Point2D.Double(centre.getX()+shiftx, centre.getY()+shifty);
							if (Double.compare(point.distanceSq(centransform),0)!=0) {
								arrw.setArrow(centransform, point, 3, 10);
							    g2.setColor(Color.yellow);
								g2.fill(arrw);
								g2.setColor(Color.magenta);
								g2.draw(arrw);
							}
							else {
								point.setLocation(1,0);
								rotat0r.transform(point, point);
								point.setLocation(point.getX()+shiftx+centre.getX(), point.getY()+shifty+centre.getY());
								arrw.setArrow(centransform, point, 3, 10);
							    g2.setColor(Color.yellow);
								g2.fill(arrw);
								g2.setColor(Color.magenta);
								g2.draw(arrw);
							}
						}
						
					}
				    
					super.paint(g);
				}
			};
			canvas.addMouseWheelListener(new MouseWheelListener() {
				
				@Override
				public void mouseWheelMoved(MouseWheelEvent e) {
					
					double scale = Math.pow(1.01, e.getWheelRotation());

					try {
						Point2D p = new Point2D.Double(e.getX(), e.getY()); 
						at.inverseTransform(p, p);
						at.concatenate(AffineTransform.getTranslateInstance(p.getX(), p.getY()));
						at.concatenate(AffineTransform.getScaleInstance(scale, scale));
						at.concatenate(AffineTransform.getTranslateInstance(-p.getX(), -p.getY()));
					}
					catch (NoninvertibleTransformException nte ) {
						nte.printStackTrace();
					}
					canvas.repaint();
				}
			});
			ff.add(canvas);
//			ff.setUndecorated(true);
			ff.pack();
			
			ff.setVisible(true);	
			
		}
		catch (IOException iox) {
			iox.printStackTrace();
		}
	}
}
